package com.xhs.hawaii.framework.thrift;

import com.xhs.hawaii.framework.zk.ZookeeperHelper;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.curator.framework.CuratorFramework;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.TServiceClientFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * @author tangchuang
 * @version 1.0
 * @since 2018/1/20
 */
public class ThriftClientProxy<T> implements InvocationHandler {

    private T proxyClient;

    private Integer maxActive = 32;

    private Integer idleTime = 180000;

    private GenericObjectPool<TServiceClient> pool;

    private Class clazz;


    public ThriftClientProxy(ZookeeperHelper zookeeperHelper, String thriftRootPath, Class clazz) throws Exception {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class<?> ifaceClass = classLoader.loadClass(clazz.getName() + "$Iface");
        this.clazz = clazz;
        // 初始化数据
        init(zookeeperHelper, thriftRootPath);

        proxyClient = (T) Proxy.newProxyInstance(classLoader, new Class[]{ifaceClass}, this);
    }

    private void init(ZookeeperHelper zookeeperHelper, String thriftRootPath) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        Class<TServiceClientFactory> factoryClass = (Class<TServiceClientFactory>) classLoader.loadClass(clazz.getName() + "$Client$Factory");
        TServiceClientFactory serviceClientFactory = factoryClass.newInstance();
        CuratorFramework zkClient = zookeeperHelper.getZkClient();
        String path = thriftRootPath + "/" + clazz.getSimpleName();
        ThriftServerAddressContainer thriftServerAddressContainer = new ThriftServerAddressContainer(zkClient, path);
        ThriftClientFactory thriftClientFactory = new ThriftClientFactory(serviceClientFactory, thriftServerAddressContainer);
        pool = new GenericObjectPool<>(thriftClientFactory, makePoolConfig());
    }


    private GenericObjectPool.Config makePoolConfig() {
        GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
        poolConfig.maxActive = maxActive;
        poolConfig.maxIdle = 1;
        poolConfig.minIdle = 0;
        poolConfig.minEvictableIdleTimeMillis = idleTime;
        poolConfig.timeBetweenEvictionRunsMillis = idleTime * 2L;
        poolConfig.testOnBorrow = true;
        poolConfig.testOnReturn = false;
        poolConfig.testWhileIdle = false;
        return poolConfig;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        TServiceClient client = pool.borrowObject();
        boolean ran = true;
        try {
            return method.invoke(client, args);
        } catch (Exception e) {
            ran = false;
            throw e;
        } finally {
            if (ran) {
                pool.returnObject(client);
            } else {
                pool.invalidateObject(client);
            }
        }
    }

    public T getObject() {
        return proxyClient;
    }
}
